#include "Function.hpp"
#include "EquationSolver.hpp"
#include <iostream>
#include <cmath>

// Constants
const double Pi = acos(-1.0);  // Define constant for Pi
const double l = 89.0;         // in inches
const double h = 49.0;         // in inches
const double beta1 = 11.5 * Pi / 180.0;  // Convert to radians

// Function to calculate A, B, C, E based on the value of D (other variables are fixed)
void calculate_ABCE(double D, double& A, double& B, double& C, double& E) {
    A = l * sin(beta1);
    B = l * cos(beta1);
    C = (h + 0.5 * D) * sin(beta1) - 0.5 * D * tan(beta1);
    E = (h + 0.5 * D) * cos(beta1) - 0.5 * D;
}

// Class F11: Represents the equation for nose-in failure f(alpha) = 0
class F11 : public Function {
private:
    double A, B, C, E;  // Parameters for the equation
public:
    F11(double A, double B, double C, double E) : A(A), B(B), C(C), E(E) {}

    double operator() (double alpha) const {
        return A * sin(alpha) * cos(alpha) + B * sin(alpha) * sin(alpha) - C * cos(alpha) - E * sin(alpha);
    }
};

// Function to solve the nose-in failure equation using Newton's Method
void solve_newton_method(double x0_deg, double D) {
    double A, B, C, E;
    calculate_ABCE(D, A, B, C, E);
    std::cout << "Solving nose-in failure equation with D = " << D << " using Newton's method, starting from " << x0_deg << " degrees" << std::endl;
    Newton_Method solver(F11(A, B, C, E), x0_deg * Pi / 180.0);  // Initialize Newton's Method solver
    double alpha = solver.solve();  // Solve for the angle alpha
    std::cout << "An approximate solution for alpha is: " << alpha * 180.0 / Pi << " degrees" << std::endl << std::endl;;  // Convert result to degrees
}
void solve_secant_method(double x0_deg, double x1_deg, double D) {
    double A, B, C, E;
    calculate_ABCE(D, A, B, C, E);
    std::cout << "Solving nose-in failure equation with D = " << D << " using Secant's method, starting from " << x0_deg << " degrees and " << x1_deg << " degrees " << std::endl;
    Secant_Method solver(F11(A, B, C, E), x0_deg * Pi / 180.0, x1_deg * Pi / 180.0);  // Initialize Secant's Method solver
    double alpha = solver.solve();  // Solve for the angle alpha
    std::cout << "An approximate solution for alpha is: " << alpha * 180.0 / Pi << " degrees" << std::endl << std::endl;  // Convert result to degrees
}

int main() {
    std::cout << "(a) D = 55 in, initial guess  33 degrees" << std::endl;
    solve_newton_method(33.0 , 55.0);  // Convert degrees to radians for initial guess

    std::cout << "(b) D = 30 in, initial guess  33 degrees" << std::endl;
    solve_newton_method(33.0 , 30.0);

    std::cout << "(c1) D = 55 in, initial guess  33 degrees and 70 degrees" << std::endl;
    solve_secant_method(33.0 , 70, 55.0);

    std::cout << "(c2) D = 55 in, initial guess  33 degrees and -11.5 degrees" << std::endl;
    solve_secant_method(33.0 , -11.5, 55.0);

    std::cout << "(c3) D = 55 in, initial guess 100 degrees and 120 degrees" << std::endl;
    solve_secant_method(100.0 , 120.0, 55.0);

    return 0;
}
